package org.wattdepot.cli.command;

import java.util.List;
import javax.xml.datatype.XMLGregorianCalendar;
import org.wattdepot.cli.processor.CommandLineInterface;
import org.wattdepot.client.MiscClientException;
import org.wattdepot.client.BadXmlException;
import org.wattdepot.client.ResourceNotFoundException;
import org.wattdepot.client.NotAuthorizedException;
import org.wattdepot.resource.source.jaxb.Source;
import org.wattdepot.util.tstamp.Tstamp;
import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * Generates an HTML file that contains a graph that displays the power generated, in MegaWatts, by
 * a particular source from the WattDepot server sampled over a particular range of time, in
 * minutes. This class extends CommandLineInterface to have access to the WattDepotClient and
 * implements WattDepotCliCommand interface to ensure this class does a specific command.
 * 
 * @author Bao Huy Ung, Edward Meyer
 * 
 */
public class ChartCommand extends CommandLineInterface implements WattDepotCliCommand {

  /**
   * Generates an HTML file that contains a graph that displays the power generated by a particular
   * source from the WattDepot server, sampled over a particular range of time.
   * 
   * @param args Is a list of all the arguments entered by the user.
   * @return a string containing the url to generate the graph, or an error message.
   */
  public String doCommand(List<String> args) {

    // These series of if-statements check if the proper key words were used in the command.
    if (args.size() != 10) {
      return "Error: Incorrect number of arguments entered. Expected 10, instead " + args.size()
          + " were entered.";
    }
    if (!args.get(1).equalsIgnoreCase("power")) {
      return "Error: Keyword \"power\" was mistyped as \"" + args.get(1) + "\".";
    }
    if (!args.get(6).equalsIgnoreCase("sampling-interval")) {
      return "Error: Keyword \"sampling-interval\" was mistyped as \"" + args.get(6) + "\".";
    }
    if (!args.get(8).equalsIgnoreCase("file")) {
      return "Error: Keyword \"file\" was mistyped as \"" + args.get(8) + "\".";
    }

    // Check if the file to be written has an HTML file extension.
    if (!args.get(9).substring(args.get(9).length() - 5).equals(".html")) {
      return "File to be written does not end with a .html file extension.";
    }

    Double power = 0.0, minPower = Double.MAX_VALUE, maxPower = Double.MIN_VALUE;
    XMLGregorianCalendar startTime = null, endTime = null, timestamp;
    Source inputSource = null;
    int maxUriLength = 2048;
    Integer interval = null;

    // Attempt to parse the start and end timestamps for the chart.
    startTime = isValidTimestamp(args.get(4));
    if (startTime == null) {
      return "Timestamp: \"" + args.get(4) + "\" cannot be parsed into a timestamp.";
    }
    endTime = isValidTimestamp(args.get(5));
    if (endTime == null) {
      return "Timestamp: \"" + args.get(5) + "\" cannot be parsed into a timestamp.";
    }

    // Check if the entered startday occurs after the endday.
    if (startTime.compare(endTime) > 0) {
      return "Invalid start and end dates. Start date occurs after the end date.";
    }

    // Check if a valid source was entered.
    inputSource = isValidSource(args.get(3));
    if (inputSource == null) {
      return "\"" + args.get(3) + "\" is not a valid source.";
    }

    // Check if valid minutes was entered.
    try {
      interval = Integer.parseInt(args.get(7));
      if (interval <= 0) {
        return "Sampling-interval must be an integer greater than 0.";
      }
    }
    catch (NumberFormatException e) {
      return "Sampling-interval is not an integer value.";
    }

    StringBuffer chartUri = new StringBuffer(maxUriLength);
    chartUri.append("http://chart.apis.google.com/chart?chs=250x100&cht=lc&chd=t:");
    timestamp = startTime;

    try {
      // Whether the graph is to show power generated or consumed, get the information at the
      // correct intervals and append to chartURL.
      // Uses the ChartPower method found at URL:
      // http://code.google.com/p/wattdepot/wiki/
      // WritingWattDepotClients#1.7_Charting_data_from_WattDepot
      if (args.get(2).equalsIgnoreCase("generated")) {
        while (Tstamp.lessThan(timestamp, endTime)) {
          // Convert to megawatts
          power = client.getPowerGenerated(args.get(3), timestamp) / 1000000;
          chartUri.append(String.format("%.1f,", power));
          if (power < minPower) {
            minPower = power;
          }
          if (power > maxPower) {
            maxPower = power;
          }
          timestamp = Tstamp.incrementMinutes(timestamp, interval);
        }
      }
      else if (args.get(2).equalsIgnoreCase("consumed")) {
        while (Tstamp.lessThan(timestamp, endTime)) {
          // Convert to megawatts
          power = client.getPowerConsumed(args.get(3), timestamp) / 1000000;
          chartUri.append(String.format("%.1f,", power));
          if (power < minPower) {
            minPower = power;
          }
          if (power > maxPower) {
            maxPower = power;
          }
          timestamp = Tstamp.incrementMinutes(timestamp, interval);
        }
      }
      // Else, unknown parameter.
      else {
        return "Invalid input at [generated|consumed] argument. Invalid input: " + args.get(2);
      }
      // Delete trailing ','
      chartUri.deleteCharAt(chartUri.length() - 1);
      chartUri.append(String.format("&chds=%.1f,%.1f&chxt=y&chxr=0,%.1f,%.1f", minPower, maxPower,
          minPower, maxPower));
    }
    catch (NotAuthorizedException e) {
      return "Operation is not authorized, please check you have sufficient permissions.";
    }
    catch (BadXmlException e) {
      return "Invalid range of timestamps were input to create graph.";
    }
    catch (MiscClientException e) {
      return "A network error occurred while communicating with the server.";
    }
    catch (ResourceNotFoundException e) {
      return "An error occurred while trying to retrieve information about client.";
    }

    // Open an output stream to write an HTML document containing an image the of graph.
    FileOutputStream fileOut = null;
    try {
      fileOut = new FileOutputStream(args.get(9));
    }
    catch (FileNotFoundException e) {
      return "Unable to create a output stream to write to file: " + args.get(8);
    }

    BufferedOutputStream writeOut = new BufferedOutputStream(fileOut);
    String breakHTML = "<br />";

    // Stores the complete string format of the output HTML document.
    String output =
        "<HTML><HEAD><TITLE>Wattdepot Chart - Google Generated</TITLE></HEAD><BODY><IMG SRC=\""
            + chartUri.toString() + "\" />" + breakHTML + "Chart generated showing power "
            + args.get(2) + breakHTML + "From source: " + args.get(3) + breakHTML
            + "On timestamp: " + args.get(4) + " to " + args.get(5) + breakHTML
            + "Sampling-interval: " + args.get(7) + " minutes" + breakHTML + "</BODY>" + breakHTML
            + "</HTML>";

    // If anything goes wrong during the write process.
    try {
      writeOut.write(output.getBytes());
      writeOut.flush();
      writeOut.close();
      fileOut.close();
    }
    catch (IOException io) {
      return "An error occured when trying to write to a file.";
    }
    return "Wrote: " + "\n" + "File: " + args.get(9) + "\n" + "To directory: "
        + System.getProperty("user.dir");

  }

}
